﻿using System;
using System.Collections;
using System.Diagnostics;
using System.Web.SessionState;
using com.bodurov.SessionRecovery.Interfaces;

namespace com.bodurov.SessionRecovery
{
    public class SessionRecoveryManager : ISessionRecoveryManager
    {
        private readonly ISessionRecoveryStorageManager _storage;
        private readonly ISessionRecoveryLogger _logger;

        private bool _ensureActiveSessionsWasCalled;

        /// <summary>
        /// The values for the interfaces will be set by the Unity Framework
        /// </summary>
        public SessionRecoveryManager(ISessionRecoveryStorageManager storage, ISessionRecoveryLogger logger)
        {
            this._storage = storage;
            this._logger = logger;
        }

        public bool IsEnabled
        {
            get
            {
                return this._logger.LogIsStillOpen;
            }
            set
            {
                // if nothing changes ignore it
                if (value == this._logger.LogIsStillOpen)
                {
                    return;
                }
                if (value)
                {
                    this._storage.ManagerIsEnabled();
                    this._logger.ManagerIsEnabled();
                    this._logger.Log(TraceLevel.Info, "SessionRecoveryManager has been ENABLED!");
                }
                else
                {
                    this._logger.Log(TraceLevel.Info, "SessionRecoveryManager has been DISABLED!");
                    this._storage.ManagerIsDisabled();
                    this._logger.ManagerIsDisabled();
                }
            }
        }

        public Hashtable RestoreIfExists(HttpSessionState session)
        {
            var hashtable = RestoreIfExists(session.SessionID);
            if (hashtable != null)
            {
                foreach (DictionaryEntry entry in hashtable)
                {
                    session[entry.Key.ToString()] = entry.Value;
                }
            }
            return hashtable;
        }

        public Hashtable RestoreIfExists(string sessionID)
        {
            if (!this._ensureActiveSessionsWasCalled)
            {
                this._storage.EnsureActiveSessions();
                this._ensureActiveSessionsWasCalled = true;
            }
            if (this._storage.NumberActiveSessions == 0 || !this._storage.ContainsActiveSession(sessionID))
            {
                return null;
            }
            try
            {
                this._logger.Log(TraceLevel.Info, "Restoring session ID='{0}'", sessionID);
                return this._storage.Recover(sessionID);
            }
            catch (Exception ex)
            {
                this._logger.Log(TraceLevel.Error,
                    "Error when restoring session ID={0} '{1}'\n{2}", sessionID, ex.Message, ex);
                throw;
            }
        }

        public void SaveIfEnabled(HttpSessionState session)
        {
            if (session.Count == 0) return;
            SaveIfEnabled(SessionToHashtable(session), session.SessionID);
        }

        public void SaveIfEnabled(Hashtable data, string sessionID)
        {
            if (!this.IsEnabled || data.Count == 0) return;
            try
            {
                this._logger.Log(TraceLevel.Info, "Saving session ID='{0}'", sessionID);
                this._storage.Save(sessionID, data);
            }
            catch (Exception ex)
            {
                this._logger.Log(TraceLevel.Error,
                    "Error when saving session ID={0} '{1}'\n{2}", sessionID, ex.Message, ex);
                throw;
            }
        }

        private static Hashtable SessionToHashtable(HttpSessionState session)
        {
            var hashtable = new Hashtable();
            foreach (var key in session.Keys)
            {
                hashtable.Add(key.ToString(), session[key.ToString()]);
            }
            return hashtable;
        }
    }
}
